#include "catalog.h"
#include "query.h"
#include "sort.h"
#include "index.h"

// INL: Indexed-Nested Loops for evaluating joins with an index on the 
//      inner/right relation (attrdesc2)
Status Operators::INL(const string & result,          // output relation name
	              const int projCnt,              // number of attributes in the projection
                      const AttrDesc attrDescArray[], // The projection list (as AttrDesc)
                      const AttrDesc & attrDesc1,     // The left attribute in the join predicate
                      const Operator op,              // The join operation
                      const AttrDesc & attrDesc2,     // The left attribute in the join predicate
                      const int reclen)              // The lenght of a tuple in the result relation
{
  cout << "Algorithm: Indexed NL Join" << endl;


	Status lStatus;
	Index Idx(attrDesc2.relName, attrDesc2.attrOffset, attrDesc2.attrLen, (Datatype)attrDesc2.attrType, false, lStatus);
	if(lStatus != OK)
		return lStatus;
	HeapFileScan outerScan(attrDesc1.relName, 0, 0, (Datatype)NULL, NULL, NOTSET,  lStatus);
	if(lStatus != OK)
		return lStatus;

	HeapFileScan innerReader(attrDesc2.relName, lStatus);
	if(lStatus != OK)
		return lStatus;

	HeapFile heapFileResult(result, lStatus);
	if(lStatus != OK)
		return lStatus;

	Status outer_scan_status = OK;
	Status inner_scan_status = OK;
	RID outerRID;
	RID innerRID;
	Record innerRec;
	Record outerRec;
	while((outer_scan_status = outerScan.scanNext(outerRID, outerRec)) == OK)
	{
		if(outer_scan_status == FILEEOF)
			break;

		if(outer_scan_status != OK)
			return outer_scan_status;

		//get filter val from outer scan and use it as the filter for the inner scan

		char filter_val[attrDesc1.attrLen];
		if(attrDesc1.attrOffset + attrDesc1.attrLen > outerRec.length)
		{
			return ATTRTOOLONG;
		}
		memcpy(filter_val, (char*)outerRec.data + attrDesc1.attrOffset, attrDesc1.attrLen); 

		//start scan for index	
	
		lStatus = Idx.startScan(filter_val);
		if(lStatus != OK)
			return lStatus;
		
		while((inner_scan_status = Idx.scanNext(innerRID)) == OK)
		{
			if(inner_scan_status == NOMORERECS)
				return OK;
			if(inner_scan_status != OK)
				return inner_scan_status;

			Status inner_status = innerReader.getRandomRecord(innerRID, innerRec);
			if(inner_status != OK)
				return inner_status;	

			
			Record result_rec;
			result_rec.length = reclen;
			result_rec.data = malloc(reclen);
			
			int result_offset = 0;
			for(int i = 0; i < projCnt; i++)
			{
				//inner check
				if(strcmp(attrDesc2.relName, attrDescArray[i].relName) == 0)
				{
					//error check
					if(result_offset + attrDesc2.attrLen > result_rec.length)
						return ATTRTOOLONG;

					memcpy((char*)result_rec.data + result_offset, (char*)innerRec.data + attrDescArray[i].attrOffset, attrDescArray[i].attrLen);
					result_offset += attrDescArray[i].attrLen;
				}
				//outer check
				else if(strcmp(attrDesc1.relName, attrDescArray[i].relName) == 0)
				{
					//error check
					if(result_offset + attrDesc1.attrLen > result_rec.length)
						return ATTRTOOLONG;

					memcpy((char*)result_rec.data + result_offset, (char*)outerRec.data + attrDescArray[i].attrOffset, attrDescArray[i].attrLen);
					result_offset += attrDescArray[i].attrLen;
				}
                else
				{
					return ATTRNOTFOUND;
				}
			}	
			RID result_rid;
			Status insert_status = heapFileResult.insertRecord(result_rec, result_rid);
			if(insert_status != OK)
				return insert_status;

			free(result_rec.data);
	
		}
		lStatus = Idx.endScan();
		if(lStatus != OK)
			return lStatus;

	}
	
	lStatus = outerScan.endScan();
	if(lStatus != OK)
		return lStatus;

  return OK;
}

